package models

import (
	"errors"
	"fmt"
	"reflect"
	"strings"
  "time"
	"github.com/astaxie/beego/orm"
)

type {{.TypeName}} struct {
{{range $i, $v := .Fields}}
 {{if $v.IsPk}}
    {{$v.GoName}}   {{$v.Type}}    `orm:"column({{$v.Name}});pk"`
 {{else if $v.NotNull}}
    {{$v.GoName}}   {{$v.Type}}    `orm:"column({{$v.Name}});notnull"`
 {{else}}
    {{$v.GoName}}   {{$v.Type}}    `orm:"column({{$v.Name}});null"`
 {{end}}
{{end}}
}

func (t *{{.TypeName}}) TableName() string {
	return "{{.TableName}}"
}

func init() {
	orm.RegisterModel(new({{.TypeName}}))
}

// Add{{.TypeName}} insert a new {{.TypeName}} into database and returns
// last inserted Id on success.
func Add{{.TypeName}}(m *{{.TypeName}}) (id int64, err error) {
	o := orm.NewOrm()
	id, err = o.Insert(m)
	return
}

// Get{{.TypeName}}ById retrieves TFlowInstance by Id. Returns error if
// Id doesn't exist
func Get{{.TypeName}}ById(id int64) (v *{{.TypeName}}, err error) {
	o := orm.NewOrm()
	v = &{{.TypeName}}{Id: id}
	if err = o.Read(v); err == nil {
		return v, nil
	}
	return nil, err
}

// Get{{.TypeName}}ByCondition retrieves TFlowInstance by Id. Returns error if
// Id doesn't exist
func Get{{.TypeName}}ByCondition(query map[string]string) ([]{{.TypeName}}, error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new({{.TypeName}}))
	condition := orm.NewCondition()
	enum := 0
	for k, v := range query {
		if strings.Contains(k, "Condition_Join") {
			if strings.Contains(v, "or") {
				enum = 1
			}

		} else {
			k = strings.Replace(k, ".", "__", -1)
			if strings.Contains(k, "isnull") {
				if enum == 0 {
					condition = condition.And(k, (v == "true" || v == "1"))
				}
				if enum == 1 {
					condition = condition.Or(k, (v == "true" || v == "1"))
				}
			} else {
				if enum == 0 {
					condition = condition.And(k, v)
				}
				if enum == 1 {
					condition = condition.Or(k, v)
					enum = 0
				}
			}
		}
	}
	qs = qs.SetCond(condition)
	result := []{{.TypeName}}{}
	_, err := qs.All(&result)
	if err != nil {
		return nil, err
	} else {
		return result, nil
	}
}


// GetAll{{.TypeName}} retrieves all {{.TypeName}} matches certain condition. Returns empty list if
// no records exist
func GetAll{{.TypeName}}(query map[string]string, fields []string, sortby []string, order []string,
	offset int64, limit int64) (ml []interface{}, count int64, err error) {
	o := orm.NewOrm()
	qs := o.QueryTable(new({{.TypeName}}))
	condition := orm.NewCondition()
	enum := 0
	for k, v := range query {
		if strings.Contains(k, "Condition_Join") {
			if strings.Contains(v, "or") {
				enum = 1
			}

		} else {
			k = strings.Replace(k, ".", "__", -1)
			if strings.Contains(k, "isnull") {
				if enum == 0 {
					condition = condition.And(k, (v == "true" || v == "1"))
				}
				if enum == 1 {
					condition = condition.Or(k, (v == "true" || v == "1"))
				}
			} else {
				if enum == 0 {
					condition = condition.And(k, v)
				}
				if enum == 1 {
					condition = condition.Or(k, v)
					enum = 0
				}
			}
		}
	}
	qs = qs.SetCond(condition)
	count, _ = qs.Count()
	// order by:
	var sortFields []string
	if len(sortby) != 0 {
		if len(sortby) == len(order) {
			// 1) for each sort field, there is an associated order
			for i, v := range sortby {
				orderby := ""
				if order[i] == "desc" {
					orderby = "-" + v
				} else if order[i] == "asc" {
					orderby = v
				} else {
					return nil, count, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
			qs = qs.OrderBy(sortFields...)
		} else if len(sortby) != len(order) && len(order) == 1 {
			// 2) there is exactly one order, all the sorted fields will be sorted by this order
			for _, v := range sortby {
				orderby := ""
				if order[0] == "desc" {
					orderby = "-" + v
				} else if order[0] == "asc" {
					orderby = v
				} else {
					return nil, count, errors.New("Error: Invalid order. Must be either [asc|desc]")
				}
				sortFields = append(sortFields, orderby)
			}
		} else if len(sortby) != len(order) && len(order) != 1 {
			return nil, count, errors.New("Error: 'sortby', 'order' sizes mismatch or 'order' size is not 1")
		}
	} else {
		if len(order) != 0 {
			return nil, count, errors.New("Error: unused 'order' fields")
		}
	}

	var l []{{.TypeName}}
	qs = qs.OrderBy(sortFields...)
	if _, err = qs.Limit(limit, offset).All(&l, fields...); err == nil {
		if len(fields) == 0 {
			for _, v := range l {
				ml = append(ml, v)
			}
		} else {
			// trim unused fields
			for _, v := range l {
				m := make(map[string]interface{})
				val := reflect.ValueOf(v)
				for _, fname := range fields {
					m[fname] = val.FieldByName(fname).Interface()
				}
				ml = append(ml, m)
			}
		}
		return ml, count, nil
	}
	return nil, count, err
}

// {{.TypeName}} updates {{.TypeName}} by Id and returns error if
// the record to be updated doesn't exist
func Update{{.TypeName}}ById(m *{{.TypeName}}) (err error) {
	o := orm.NewOrm()
	v := {{.TypeName}}{Id: m.Id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Update(m); err == nil {
			fmt.Println("Number of records updated in database:", num)
		}
	}
	return
}

// Delete{{.TypeName}} deletes {{.TypeName}} by Id and returns error if
// the record to be deleted doesn't exist
func Delete{{.TypeName}}(id int64) (err error) {
	o := orm.NewOrm()
	v := {{.TypeName}}{Id: id}
	// ascertain id exists in the database
	if err = o.Read(&v); err == nil {
		var num int64
		if num, err = o.Delete(&{{.TypeName}}{Id: id}); err == nil {
			fmt.Println("Number of records deleted in database:", num)
		}
	}
	return
}

